package top.superflow.json.util

import java.io.File

import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.module.jsonSchema.{JsonSchema, JsonSchemaGenerator}
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import top.superflow.exception.{Exceptable, RequestUnsupportedException}
import top.superflow.lang.StringUtils
import top.superflow.utils.FileUtils

object JsonUtils extends Exceptable{
  private val mapper = new ObjectMapper
  mapper.registerModule(DefaultScalaModule)
  mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true)

  def readJson[T](json : Any, valueType : Class[T] ) : T = {
    var jsonString : String = getJson(json)

    mapper.readValue(jsonString, valueType)
  }

  def getJson(json : Any) : String = {
    if(json == null){
      return "{}"
    }
    if(FileUtils.isFile(json)){
       return FileUtils.readText(FileUtils.getFile(json))
    }

    else if(JsonUtils.isJson(json.toString)){
      return json.toString
    }

    "{}"
  }

  def readJson[T](file : Any, valueType : Class[T], template : String ) : T = {
    if(template == null){
      return readJson[T](file, valueType)
    }

    val tmpObj = mapper.readValue(template, classOf[Nothing])
    val updater = mapper.readerForUpdating(tmpObj)

    val fl = FileUtils.getFile(file)
    updater.readValue(fl)
  }

  def merge( template : String, subJson : String ) : String = {
    if(StringUtils.isBlank(template)){
      return subJson
    }

    if(!isJson(template)){
      throwRequestUnsupportedException("The template {} should be a json!", template)
    }

    if(!isJson(subJson)){
      throwRequestUnsupportedException("The sub json {} should be a json!", subJson)
    }

    val tmpObj = getObjectNode(template)
    val subObj = getObjectNode(subJson)

    val mergedObj = merge(subObj,tmpObj)

    mapper.writeValueAsString(mergedObj)
  }

  def getField(json : String, fieldName : String) : String = {
    val objectNode = getObjectNode(json)
    if(objectNode == null){
      return null
    }

    val field = objectNode.get(fieldName)
    if(field == null){
      return null
    }

    field.asText()
  }

  private def merge(from : ObjectNode, to : ObjectNode): ObjectNode ={
    if(from == null){
      return to
    }

    if(to == null){
      return from
    }

    from.fields().forEachRemaining(entry => {
      to.set(entry.getKey, entry.getValue)
    })

    to
  }

  def getObjectNode(jsonString : String) : ObjectNode = {
    val jsonTree = mapper.readTree(jsonString)
    if(jsonTree.isObject){
      return jsonTree.asInstanceOf[ObjectNode]
    }

    null
  }

  def isJson(jsonString : String ) : Boolean = {
    val actualObj = mapper.readTree(jsonString)
    actualObj != null
  }


  def readJson(file : Any) : Map[String, String] = {
    file match {
      case fpath : String => {
        readJson(new File(fpath))
      }

      case fl : File => {
        mapper.readValue(fl, classOf[Map[String, String]])
      }

      case _ => {
        throw new RequestUnsupportedException(file)
      }
    }
  }

  def generateJsonSchema(clazz : Class[_]) : String = {
    if(clazz == null){
      throw new RequestUnsupportedException("The class should not be null")
    }

    val objectMapper = new ObjectMapper
    val jsonSchemaGenerator = new JsonSchemaGenerator(objectMapper)
    val jsonSchema: JsonSchema = jsonSchemaGenerator.generateSchema(clazz)

    val jsonSchemaAsString:String = objectMapper.writeValueAsString(jsonSchema)

    jsonSchemaAsString
  }


}